#include <QMutex>
#include <QMetaClassInfo>
#include "QRemoteObject2Meta_P.h"
#include "QRemoteObject2.h"
#include "QROHost.h"
#include "QRONode.h"
#include "QHierarchyFactory_p.h"
#include <rep_RemoteInterface_source.h>


static QRemoteObject2::ROSignalBehavior StringToSignalFlag(QString input){
    if(input == REMOTE_PROP_FLAG_SERVER_ONLY)
    {
        return QRemoteObject2::ROSignalBehavior::ServerOnly;
    }
    else if(input == REMOTE_PROP_FLAG_CLIENT_ONLY)
    {
        return QRemoteObject2::ROSignalBehavior::ClientOnly;
    }
    else if(input == REMOTE_PROP_FLAG_OWNER_ONLY)
    {
        return QRemoteObject2::ROSignalBehavior::OwnerOnly;
    }
    else if(input == REMOTE_PROP_FLAG_BROAD_CAST)
    {
        return QRemoteObject2::ROSignalBehavior::BroadCast;
    }
    return QRemoteObject2::ROSignalBehavior::None;
}

static QRemoteObject2::ROPropertyFlag StringToPropertyFlag(QString input){
    if(input == REMOTE_PROP_FLAG_READONLY)
    {
        return QRemoteObject2::ROPropertyFlag::ReadOnly;
    }
    else if(input == REMOTE_PROP_FLAG_READWRITE)
    {
        return QRemoteObject2::ROPropertyFlag::ReadWrite;
    }
    return QRemoteObject2::ROPropertyFlag::None;
}

class QRemoteObject2Private{
public:
    static void autoRegisterMetaInfo(QRemoteObject2::RemoteObjectMetaData* data){
        auto meta = data->meta;

        int classInfoCount = meta->classInfoCount();
        QString signalPrefix = "QtRO.Method.";
        QString propPrefix = "QtRO.Property.";
        for(auto i = meta->classInfoOffset();i<classInfoCount;i++)
        {
            auto metaInfo = meta->classInfo(i);
            QString name = metaInfo.name();
            QString value = metaInfo.value();

            if(name.startsWith(signalPrefix))
            {
                QString signalName = name.mid(signalPrefix.size());

                auto funIndex = meta->indexOfMethod(signalName.toLocal8Bit());
                if(funIndex == -1)
                {
                    continue;
                }

                QRemoteObject2::ROMethodMetaData prop;
                prop.index = funIndex;
                prop.behaiovr = StringToSignalFlag(value);
                data->methods[funIndex] = prop;

            }
            else if(name.startsWith(propPrefix))
            {
                QString propName = name.mid(propPrefix.size());

                auto propIndex = meta->indexOfProperty(propName.toLocal8Bit());
                if(propIndex == -1)
                {
                    continue;
                }

                QRemoteObject2::ROPropertyMetaData prop;
                prop.index = propIndex;
                prop.flag = StringToPropertyFlag(value);
                data->propertys[propIndex] = prop;
            }
        }



    }

    static void registerParentInterface(QRemoteObject2::RemoteObjectMetaData* data){
        //寻找该类是否有默认父级访问器
        auto factory = QHierarchtFactoryManager::Instance()->findHierarchyFactory(data->meta);
        if(!factory)
        {
            qDebug()<<"找不到该类的父级访问接口";
            return;
        }

        data->hierarchyInterface.reset(factory->createInstance(data->meta));
    }

    static int findMethod(const QMetaObject* meta,QString name){
        for(int i = 0;i<meta->methodCount();i++)
        {
            if(meta->method(i).name() == name)
            {
                return i;
            }
        }

        return -1;
    }

};

QRemoteObject2::RemoteClassRegister QRemoteObject2::registerRemoteClass(const QMetaObject *meta,std::function<QObject*()> constructor)
{
    return RemoteClassRegister(meta,constructor);
}

void QRemoteObject2::setParent(QObject *target, QObject *parent)
{
    auto net = getNetInterface(target);

    if(net == NULL)
        return;

    net->pushParent(target,parent);
}

void QRemoteObject2::occupyObject(QObject *target)
{
    auto net = getNetInterface(target);

    if(net == NULL)
        return;
    net->occupyObject(target);
}

void QRemoteObject2::releaseObject(QObject *target)
{
    auto net = getNetInterface(target);

    if(net == NULL)
        return;
    net->releaseObject(target);
}

QObject *QRemoteObject2::occupiedObject(QObject* target)
{
    auto net = dynamic_cast<QRONode*>(getNetInterface(target));

    if(net == NULL)
    return NULL;


    return net->getOccupiedObject();
}

void QRemoteObject2::pushProperty(QObject *target, int index, QVariant value)
{
    auto net = getNetInterface(target);

    if(net == NULL)
        return;

    net->pushProperty(target,index,value);
}

void QRemoteObject2::invoke(QObject *target, int index, QVariantList args, InvokeCallBack callBack)
{
    auto net = getNetInterface(target);

    if(net == NULL)
        return;

    net->activeMethod(target,index,args,callBack);
}

QROInterface *QRemoteObject2::getNetInterface(QObject *target)
{
    QObject *ret = target->property("__RO_Net_Obj").value<QObject*>();
    return dynamic_cast<QROInterface*>(ret);
}

bool QRemoteObject2::isSource(QObject *target)
{
    auto net = getNetInterface(target);
    return dynamic_cast<QROHost*>(net);
}

bool QRemoteObject2::isReplica(QObject *target)
{
    auto net = getNetInterface(target);
    return dynamic_cast<QRONode*>(net);
}

void QRemoteObject2::unregister(QObject *target)
{
    auto net = getNetInterface(target);

    if(net == NULL)
        return;

    auto host = dynamic_cast<QROHost*>(net);

    if(host)
    {
        host->unregisterObject(target);
        return;
    }

    auto node = dynamic_cast<QRONode*>(net);
    if(node)
    {
        node->unregisterObject(target);
        return;
    }
}

void QRemoteObject2::registerHierarchyFactory(const char *className, HierarchyInterfaceFactory *factory)
{
    QHierarchtFactoryManager::Instance()->registerHierarchyFactory(className,factory);
}

void QRemoteObject2::RemoteClassRegister::addSignal(int index, ROSignalBehavior behavior)
{
    ROMethodMetaData prop;
    prop.index = index;
    prop.behaiovr = behavior;
    data->methods[index] = prop;
}

void QRemoteObject2::RemoteClassRegister::addProperty(int index, ROPropertyFlag flag)
{
    ROPropertyMetaData prop;
    prop.index = index;
    prop.flag = flag;
    data->propertys[index] = prop;
}

void QRemoteObject2::RemoteClassRegister::finish()
{
    QRemoteObject2Private::registerParentInterface(data.get());
    QRemoteObject2Meta::Instance()->registerRemoteClass(data);
}

QRemoteObject2::RemoteClassRegister::RemoteClassRegister(const QMetaObject *meta,std::function<QObject*()> constructor)
{
    data = std::make_shared<RemoteObjectMetaData>();
    data->meta = meta;
    data->constructor = constructor;
    QRemoteObject2Private::autoRegisterMetaInfo(data.get());
}
